package com.yungu.swift.admin.base;

import com.yungu.swift.base.ResponseData;
import com.yungu.swift.base.model.BaseDto;
import com.yungu.swift.base.model.BaseVo;
import com.yungu.swift.base.model.PageVo;
import com.yungu.swift.base.model.annotation.AbsoluteImgPath;
import com.yungu.swift.base.model.annotation.ResponseImgOverride;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;

import static com.yungu.swift.admin.config.NacosListener.SYS_CONFIG;

/**
 * 对有 @AbsoluteImgPath 注解的字段补充前缀信息
 *
 * @author : cuixiuyin
 * @date : 2019/7/11
 */
@Slf4j
@RestControllerAdvice
@Order(-1)
public class ResponseImgAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return returnType.hasMethodAnnotation(ResponseImgOverride.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof ResponseData) {
            Object data = ((ResponseData) body).getData();
            if (data == null) {
                return body;
            }
            if (data instanceof PageVo) {
                data = ((PageVo) data).getItems();
            }
            if (data instanceof Collection && CollectionUtils.isNotEmpty((Collection<Object>) data)) {
                fillPicturePrefixCollection((Collection<Object>) data);
                return body;
            }
            if (data instanceof Map) {
                Collection values = ((Map) data).values();
                fillPicturePrefixCollection(values);
                return body;
            }
            fillPicturePrefix(data);
        }
        return body;
    }


    private void fillPicturePrefixCollection(Collection<Object> objects) {
        for (Object obj : objects) {
            fillPicturePrefix(obj);
        }
    }

    private void fillPicturePrefix(Object obj) {
        if (obj == null) {
            return;
        }
        Class<?> aClass = obj.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            try {
                field.setAccessible(true);
                Object fieldObj = field.get(obj);
                if (fieldObj == null) {
                    continue;
                }
                // 判断非基本数据类型，是 Dto/Vo 的情况
                if (BaseDto.class.isAssignableFrom(field.getType()) || BaseVo.class.isAssignableFrom(field.getType())) {
                    fillPicturePrefix(fieldObj);
                }
                // 判断 Collection 类型
                if (Collection.class.isAssignableFrom(field.getType())) {
                    fillPicturePrefixCollection((Collection<Object>) fieldObj);
                }
                // 判断 Map 类型
                if (Map.class.isAssignableFrom(field.getType())) {
                    Map fieldObjMap = (Map) fieldObj;
                    fillPicturePrefixCollection(fieldObjMap.values());
                }
                fillFieldPicturePrefix(field, obj);
            } catch (IllegalAccessException e) {
                log.error("Class Method invoke IllegalAccessException fieldName:{}.", field.getName(), e);
            }
        }
    }


    private void fillFieldPicturePrefix(Field field, Object obj) throws IllegalAccessException {
        AbsoluteImgPath absoluteImgPath = field.getAnnotation(AbsoluteImgPath.class);
        if (absoluteImgPath != null && field.get(obj) != null) {
            field.setAccessible(true);
            field.set(obj, SYS_CONFIG.getWebSite() + field.get(obj));
        }
    }
}
